package yesCaptcha

import (
	"context"
	"encoding/json"
	"fmt"
	"gitee.com/kaylee595/gkaylee/kerr"
	"github.com/imroc/req/v3"
)

type Client struct {
	httpClient *req.Client
	clientKey  string
	softId     int
}

type Option func(*Client)

func WithHttpClient(httpClient *req.Client) Option {
	return func(c *Client) {
		if httpClient == nil {
			httpClient = req.C()
		} else {
			httpClient = httpClient.Clone()
		}
		httpClient = httpClient.SetBaseURL(c.httpClient.BaseURL)
		c.httpClient = httpClient
	}
}

func WithSoftId(softId int) Option {
	return func(c *Client) {
		c.softId = softId
	}
}

func WithRegion(r *Region) Option {
	return func(c *Client) {
		c.httpClient.SetBaseURL(r.String())
	}
}

var _, _, _ = WithHttpClient, WithSoftId, WithRegion

func NewClient(clientKey string, opts ...Option) *Client {
	client := &Client{
		httpClient: req.C().SetBaseURL(RegionChina.String()),
		clientKey:  clientKey,
	}
	for _, opt := range opts {
		opt(client)
	}
	return client
}

type BaseRequest struct {
	ClientKey string `json:"clientKey,omitempty"` // 帐户密钥，可以在个人中心找到
	SoftID    int    `json:"softId,omitempty"`    // 软件ID
	TaskId    string `json:"taskId,omitempty"`    // 任务ID
	Task      any    `json:"task,omitempty"`      // 任务信息
}

type BaseResponse struct {
	ErrorId          int    `json:"errorId"`          // 0 - 没有错误，1 - 有错误
	ErrorCode        string `json:"errorCode"`        // 错误代码，参考: https://yescaptcha.atlassian.net/wiki/spaces/YESCAPTCHA/pages/5603341/errorCode
	ErrorDescription string `json:"errorDescription"` // 错误详细描述
}

func (b *BaseResponse) IsError() bool {
	return b.ErrorId != 0
}

func (b *BaseResponse) Error() string {
	return fmt.Sprintf("errorId: %d, errorCode: %s, errorDescription: %s", b.ErrorId, b.ErrorCode, b.ErrorDescription)
}

type RespCreateTask struct {
	BaseResponse
	TaskId string `json:"taskId"` // 创建的任务ID
}

// CreateTask
// task: recaptchaV2.Task recaptchaV3.Task
func (c *Client) CreateTask(ctx context.Context, task any) (*RespCreateTask, error) {
	const url = "/createTask"
	ret := &RespCreateTask{}
	resp := c.httpClient.Post(url).SetBodyJsonMarshal(&BaseRequest{ClientKey: c.clientKey, SoftID: c.softId, Task: task}).SetSuccessResult(ret).Do(ctx)
	if resp.Err != nil {
		return nil, kerr.WrapTraceStack(resp.Err)
	}
	if ret.IsError() {
		return nil, kerr.WrapTraceStack(ret)
	}
	return ret, nil
}

type RespGetTaskResult[T any] struct {
	BaseResponse
	Status   TaskStatus `json:"status"`
	Solution T          `json:"solution"`
}

func (c *Client) GetTaskResult(ctx context.Context, taskId string) (*RespGetTaskResult[json.RawMessage], error) {
	const url = "/getTaskResult"
	ret := &RespGetTaskResult[json.RawMessage]{}
	resp := c.httpClient.Post(url).SetBodyJsonMarshal(&BaseRequest{ClientKey: c.clientKey, TaskId: taskId}).SetSuccessResult(ret).Do(ctx)
	if resp.Err != nil {
		return nil, kerr.WrapTraceStack(resp.Err)
	}
	if ret.IsError() {
		return nil, kerr.WrapTraceStack(ret)
	}
	return ret, nil
}
